<?php

declare(strict_types=1);

/**
 * Author: Jesse
 * Email : jessedev@163.com
 * Date: 2024/6/18 09:48
 * FileName: AbstractServiceClient.php
 * DESC: The Relentless Pursuit of Perfection.
 */

namespace Common\Kernel\Rpc;

use Hyperf\Collection\Arr;
use Hyperf\Contract\IdGeneratorInterface;
use Hyperf\Contract\NormalizerInterface;
use Hyperf\RpcClient\Exception\RequestException;
use Hyperf\RpcClient\ServiceClient as BaseServiceClient;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;

/**
 * Rpc 客户端
 */
class ServiceClient extends BaseServiceClient
{
    /**
     * @var NormalizerInterface
     */
    private NormalizerInterface $normalizer;

    /**
     * @param ContainerInterface $container
     * @param string $serviceName
     * @param string $protocol
     * @param array $options
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function __construct(ContainerInterface $container, string $serviceName, string $protocol = 'jsonrpc-http', array $options = [])
    {
//        $options['_jsonrpc_service_name'] = $this->serviceName;
        parent::__construct($container, $serviceName, $protocol, $options);

        $this->normalizer = $this->client->getNormalizer();
    }

    /**
     * 客户端请求
     */
    protected function __request(string $method, array $params, ?string $id = null)
    {
        // 生成唯一请求ID
        if ($this->idGenerator instanceof IdGeneratorInterface && !$id) {
            $id = $this->idGenerator->generate();
        }

        // 客户端请求
        $response = $this->client->send($this->__generateData($method, $params, $id));

        // 非法响应
        if (!is_array($response)) {
            throw new RequestException('Invalid response.');
        }

        // 校验请求
        $response = $this->checkRequestIdAndTryAgain($response, $id);

        // 响应结果
        if (array_key_exists('result', $response)) {
            // 判断返回的类型是不是与接口一致
            $type = $this->methodDefinitionCollector->getReturnType($this->serviceInterface, $method);
            // 判断是不是可以返回null
            if ($type->allowsNull() && $response['result'] === null) {
                return null;
            }
            // 正常数据处理
            return $this->normalizer->denormalize($response['result'], $type->getName());
        }

        // 错误响应处理
        if ($code = $response['error']['code'] ?? null) {
            $error = $response['error'];
            // Denormalize exception.
            $class = Arr::get($error, 'data.class');
            // Error Data
            $errData = [
                'code' => Arr::get($error, 'data.code', []),
                'message' => Arr::get($error, 'data.message', []),
            ];
            // 将来的异常类抛出服务器抛出BusinessException，这里抛出到异常处理器上
            if (isset($class) && class_exists($class) && $e = $this->normalizer->denormalize($errData, $class)) {
                if ($e instanceof Throwable) {
                    throw $e;
                }
            }
            // Throw RequestException when denormalize exception failed.
            // TODO 原来默认的也可以改成抛出异常类，先不改目前不影响。
            throw new RequestException($error['message'] ?? '', $code, $error['data'] ?? []);
        }

        throw new RequestException('Invalid response.');
    }
}